<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.9.4"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Flow-IPC: Sessions: Teardown; Organizing Your Code</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
 <tbody>
 <tr id="projectrow">
  <td id="projectalign">
   <div id="projectname">Flow-IPC<span id="projectnumber">&#160;1.0.2</span>
   </div>
   <div id="projectbrief">Flow-IPC project: Public API.</div>
  </td>
 </tr>
 </tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.9.4 -->
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&amp;dn=expat.txt MIT */
var searchBox = new SearchBox("searchBox", "search",'Search','.html');
/* @license-end */
</script>
<script type="text/javascript" src="menudata.js"></script>
<script type="text/javascript" src="menu.js"></script>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&amp;dn=expat.txt MIT */
$(function() {
  initMenu('',true,false,'search.php','Search');
  $(document).ready(function() { init_search(); });
});
/* @license-end */
</script>
<div id="main-nav"></div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
     onmouseover="return searchBox.OnSearchSelectShow()"
     onmouseout="return searchBox.OnSearchSelectHide()"
     onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>

<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0" 
        name="MSearchResults" id="MSearchResults">
</iframe>
</div>

</div><!-- top -->
<div><div class="header">
  <div class="headertitle"><div class="title">Sessions: Teardown; Organizing Your Code </div></div>
</div><!--header-->
<div class="contents">
<div class="textblock"><center><b>MANUAL NAVIGATION:</b> <a class="el" href="chan_open.html">Preceding Page</a> - <a class="el" href="safety_perms.html">Next Page</a> - <a href="./pages.html"><b>Table of Contents</b></a> - <a class="el" href="namespaceipc.html"><b>Reference</b></a></center><hr  />
<p >Here we discuss the important task of handling session-ending events (errors); and recommended an approach to structuring your subject-to-IPC objects within your application code. (Or go back to preceding page: <a class="el" href="chan_open.html">Sessions: Opening Channels</a>. The penultimate-to-us <a class="el" href="session_setup.html">page</a> may be even more directly relevant however.)</p>
<h2>Page summary </h2>
<p >First, a reminder: A session is a conversation between process A, of application Ap, and process B of Bp. Once open, the two processes are equal in their capabilities (in the context of IPC).</p>
<p >Thus a session's lifetime is (omitting non-IPC-related setup/teardown for simplicity of discussion) the maximum time period when both the given processes A and B are up. Therefore the session is closed at the earliest time such that one of the processes A or B exits gracefully (<code>exit()</code>), exits ungracefully (<code>abort()</code>), or enters an unhealthy/stuck state of some kind (zombification).</p>
<p >Therefore, a session's closure in a <em>given</em> process P has two basic <b>trigger types</b>:</p><ul>
<li><b>Locally triggered</b>: P itself wants to exit &ndash; gracefully, via <code>exit()</code>, for example/typically due to receiving SIGTERM or equivalent. (We assume a process never "wants" to exit ungracefully or zombify, and even if it's retained some measure of control in that event, it cannot be worried about gracefully destroying <code>Session</code> objects on purpose.)<ul>
<li>In this case, by definition, it shall close the local <code>Session</code> (etc.; details below) and then exit. I.e., there's no need to start another session.</li>
</ul>
</li>
<li><b>Partner-triggered</b>: P <em>detects</em> that the <em>opposing</em> process is <code>exit()</code>ing, <code>abort()</code>ing, or has become zombified/unhealthy.<ul>
<li>In this case, assuming (as we do) the process wants to continue being useful &ndash; i.e., engage in IPC &ndash; it may start more sessions, depending on its identity (session-client versus session-server). We've touched on this already in <a class="el" href="session_setup.html">Sessions: Setting Up an IPC Context</a> example code. To summarize:<ul>
<li>P is B (session-client): It should create a new <code>Session</code> and attempt to connect to the session-server. A session-client engages in at most one session (IPC conversation, IPC context) at a time.</li>
<li>P is A (session-server): In the simplest scenario, where the server is coded to only talk to one partner process at a time, its task is the same as for B, a client process. In the more complex scenario, it already has a loop in which it accepts any incoming session-opens on-demand, even while other session(s) is/are operating. Therefore in that case it need not do anything further.</li>
</ul>
</li>
</ul>
</li>
</ul>
<p >That said:</p>
<h3>Topic 1: Session teardown</h3>
<p >In <a class="el" href="session_setup.html">Sessions: Setting Up an IPC Context</a>, we talked plenty about how to <em>open</em> a session, but we almost completely skipped over <em>closing</em> it. In particular when it came time to supply a session error handler (in <code>Client_session</code> constructor and <code>Session_server::init_handlers()</code>), our example code said <code>...</code> and punted to the present Manual page. Explaining this, in and of itself, is straightforward enough; and we will do so below.</p>
<h3>Topic 2: Organizing application code around sessions</h3>
<p >However conceptually that topic touches on another, much less clear-cut, discussion: How to organize one's application code (on each side) in such a way as to avoid making the program an unmaintainable mess, particularly around session deinitialization time. We did, briefly, mention that after the session is closed, on the client one would attempt to open another, identically; and on the server potentially one would do so immediately after <em>opening</em> a session, so that multiple sessions at a time could be ongoing. How to make all this happen in code? How to make the session-client and session-server code as mutually symmetrical as possible? How to keep one session's IPC resources (channels, SHM arenas, SHM-allocated objects) segregated in code from another session's? That is the topic here. <em>Please understand that this is informal advice, which you're free to ignore or modify for yourself.</em> In many cases tutorial-style documentation of a library like this would omit such advice; but we felt it important to help you save pain and time. As long as you understand the reasoning here, we feel this page's mission is accomplished.</p>
<h2>The mechanics of session teardown </h2>
<p >So let's get into the relatively easy topic: the API for closing an open session.</p>
<p >If the closing trigger is <em>local</em>, then one simply destroys the <code>Session</code> object (its destructor getting called), or in the case of the server side potentially 1+ <code>Session</code> object(s). That's it. Then it can exit the process.</p>
<dl class="section note"><dt>Note</dt><dd>When locally-triggered (process is exiting gracefully), a session-server should, also, destroy the <code>Session_server</code> object (its destrctuctor getting called). The order does not matter; for example <code>Session_server</code> can be destroyed first.</dd></dl>
<p>If the closing trigger is the peer <em>partner</em>, then one... also simply destroys the one, relevant <code>Session</code> object. Then one either attempts to start another session (if a session-client, or server acting equally to a client), or does nothing (if acting as a server that can accept arbitrary number of concurrent sessions anyway).</p>
<p >Any <code>Session</code> or <code>Session_server</code> destructor has essentially synchronous effects. There is no lagging activity of which one need to be particularly aware.</p>
<hr  />
<dl class="section user"><dt>Kernel-persistent resource cleanup</dt><dd>"Kernel-persistent resources" are areas in RAM that are not necessarily given back to the OS for general use when a process accessing them dies; instead they're only guaranteed to be made available on next boot. (As of this writing Flow-IPC potentially acquires the following types of these resources: SHM pools (a/k/a segments); POSIX MQs.) Naturally it is important that these be returned to the OS at the proper time; otherwise it's no less than a RAM leak that could persist beyond any process that was using the RAM. How is this topic (which we call <b>cleanup</b>) handled in Flow-IPC's context? Answer:</dd></dl>
<dl class="section user"><dt></dt><dd>Short version: It's handled. You need not worry about it. (That is one of the features of the library that's not so easy to guarantee manually and in fact much harder than it would seem, until one has to actually design/code.)</dd></dl>
<dl class="section user"><dt></dt><dd>Longer version, for your general curiosity only (as these are internal impl items): You can observe the various cleanup steps in INFO-level log messages. These show the following:</dd></dl>
<dl class="section user"><dt></dt><dd>In case all processes involved shut down gracefully (usually via <code>exit()</code>), resources are freed as soon as no sessions needing them are up (nor, for certain cross-session resources, can start in the future) but no earlier. In particular the major points where this is achieved are: <code>Session</code> destructor (any per-session resources for that session); <code>Session_server</code> destructor (any cross-session resources). In most cases both resource acquisition and resource cleanup is performed (internally) in session-server code as opposed to session-client. (As of this writing the exception to this is SHM-jemalloc which does things in a more equitable/symmetrical fashion, since internally each side creates/controls its own SHM arena, a/k/a SHM pool collection, from which the other side "borrows" individual allocated objects.)</dd></dl>
<dl class="section user"><dt></dt><dd>In case of ungraceful shutdown, usually via <code>abort()</code>: The RAM may leak temporarily; but it will be cleaned-up zealously once a process of the same applications Ab/Bp next starts. In most cases left-behind-due-to-abort items are cleaned once the <code>Session_server</code> in application Ap (the session-server-assigned <code>App</code>) is created, synchronously in its constructor. This is safe, since only one session-server for given app Ap is to be active at a time. (As of this writing, again, SHM-jemalloc is the exception to this. In its case any process of application Ap or Bp shall regularly clean-up any SHM arenas/pools created by those processes of its own application Ap or Bp, respectively, that are reported by the OS to be no longer running. The mechanism for checking this, in Linux, is to "send" fake signal 0 to the given process-ID and observe the result of the syscall.)</dd></dl>
<hr  />
<p >Okay, but &ndash; in the case of partner-triggered session closure &ndash; how do we detect that the partner has indeed closed it, either gracefully or ungracefully, or has become become unhealthy/zombified? This is more complex than destroying the <code>Session</code> (plus possibly other <code>Session</code>(s) and/or <code>Session_server</code>) that follows it, but it's also done via a well-defined API. To wit: this is the error handler which we've omitted in (<a class="el" href="session_setup.html">Sessions: Setting Up an IPC Context</a>) examples so far. As shown in that Manual page, the error handler is supplied to <a class="el" href="namespaceipc_1_1session.html#a77d8fda00e1e17f595a6a5d4c44375e7"><code>Client_session</code> constructor</a> or <a class="el" href="classipc_1_1session_1_1Server__session__mv.html#a99097e5aa6883877c74f8a93ebc9f3e1">Server_session::init_handlers()</a>. Recall these both must be called strictly before the session is officially considered opened.</p>
<p ><code>Session</code> keeps track of partner-triggered session closure, which we term the <b>session becoming hosed</b> (a/k/a <b>session-hosing conditions</b>). Yes, really. Once detected, it <em>immediately</em> triggers the error handler you supplied. Since it is potentially important for your peace of mind, at least the following (internal) conditions will lead to session-hosing:</p><ul>
<li>An internally maintained (for various needs but most importantly channel-opening negotiations) <em>session master channel</em> uses a local (Unix-domain) stream socket connection; and that socket connection has become gracefully, or ungracefully, closed by the opposing side. A graceful closure would (internally) involve a TCP-FIN-like end-of-"file" condition being received from the opposing side; usually indicating a clean <code>exit()</code>. An ungraceful closure would (internally) involve a TCP-RST-like error condition (ECONNRESET, EPIPE) being received from the opposing side; usually indicating an <code>abort()</code> or similar.<ul>
<li>This (internally) occurs, at the <em>latest</em> as part of the OS tearing down the opposing process, as thus is generally rather responsive. There is still time for chaos in the ungraceful-shutdown scenario, but generally it's pretty quick, once things go down.</li>
</ul>
</li>
<li>Some other error occurred while operating along that same (internally maintained) socket connection, either when sending or receiving internal data. The aforementioned end-of-"file," connection-reset, broken-pipe are the conditions we typically see in practice.</li>
<li>That same (internally maintained) connection remains open without system errors popping up, but no (internally tracked) keep-alive messages have been received for some time (low seconds). Probably the opposing process has become zombified or otherwise unhealthy.<ul>
<li>This is, by design, a heuristic; the time (seconds) of having received no traffic (user-triggered or internal pings) is non-trivial. During this time the opposing process may have already been unresponsive for a significant portion of it, so there's a higher chance of entropy in this case. Hopefully it does not come to that, and the harder-edged socket error path described above is the one detected the vast majority of the time.</li>
</ul>
</li>
</ul>
<p >In any case an <code>Error_code</code> passed to your error handler will indicate the triggering condition, while logs (at least WARNING message(s) by Flow-IPC) will contain further details. The <code>Error_code</code> itself is for your logging/reporting/alerting purposes only; it is not advised to make algorithmic decisions based on it. If the error handler was invoked, the session <em>is</em> hosed, period.</p>
<p >We defer an example of registering an error handler until the following section, as it's very much relevant to:</p>
<h2>Organizing application code around sessions </h2>
<p >The Manual author(s) must stress that much of the following does not represent hard rules or even (necessarily) verbatim recommendations, in particular text from <a class="el" href="session_app_org.html#recs">this point</a> on. The point is to encourage certain basic principles which we feel shall make your life, and that of maintainers of your code, easier than otherwise. Undoubtedly you will and should modify the below recommendations and especially code snippets to match your specific situation. That said &ndash; it would be unwise to ignore it entirely.</p>
<p ><a class="anchor" id="scope"></a></p><h3>Basic concept: data scope</h3>
<p >The basic problem we are solving here is this: You have your algorithms and data structures, in your meta-application Ap-Bp, and in service of these it is required that you perform IPC. To perform IPC, in the <a class="el" href="namespaceipc_1_1session.html" title="Flow-IPC module providing the broad lifecycle and shared-resource organization – via the session conc...">ipc::session</a> paradigm, Flow-IPC <em>requires</em> that you use its abtractions, most notably those of <code>Session_server</code>, <code>Server_session</code> (on open, just <code>Session</code>), and <code>Client_session</code> (ditto). How to organize your code given your existing algorithms/structures and the aforementioned Flow-IPC abstractions?</p>
<p >The key point is that each of your own (IPC-relevant) data structures (and probably related algorithms) should be very cleanly and explicitly classified to have a particular <em>scope</em>, a/k/a lifetime. Generally, the scope of each (IPC-relevant) datum is one of the following:</p><ul>
<li><b>[Per-]session scope</b>: The datum begins life no earlier than the creation of the (<em>opened</em>!) <code>Session</code> (A-B process-to-process conversation) and no later than the (opened) <code>Session</code>'s destruction. If the session ends (as earlier noted, when the process exits &ndash; or, far more interestingly, when the <em>opposing</em> process exits or dies or gets zombified), then this datum is no longer relevant by definition: The process reponsible for ~half of the algorithm that uses the datum is simply out of commission permanently, at a minimum.</li>
<li><b>Cross-session scope</b> a/k/a <b>app scope</b>: The datum may be accessed by possibly multiple sessions concurrently; and possibly by sessions that do not yet exist. For example, an in-memory cache of web objects might be relevant to any processes that might connect later to make use of (and add to) the cache, not to mention any processes currently connected into the IPC-engaged system.<ul>
<li>Consider a given <code>Session_server</code>. App-scope data, if they even exist in your meta-application at all, do not simply apply to all sessions to start via this <code>Session_server</code>. Instead each cross-section datum must pertain to a particular partner (client) <em>application</em> (not process &ndash; that'd be per-session). For example, if your server Ap supports (as listed in <a class="el" href="structipc_1_1session_1_1Server__app.html#a12df649b33893270afeac7240a571633" title="A given Client_app (as identified by its distinct App::m_name) may request to establish an IPC sessio...">ipc::session::Server_app::m_allowed_client_apps</a>) two possible partner applications Bp and Cp, then a given datum must be classified (by you) to be either per-app-B or per-app-C.</li>
<li>Such a datum begins life no earlier than the <em>first</em> session pertaining to the particular <code>Client_app</code> becoming open (which can occur only after <code>Session_server</code> construction). Its life ends no later than the <code>Session_server</code>'s destruction.</li>
<li><em>Note well: Flow-IPC does NOT allow for IPC-relevant, <a class="el" href="namespaceipc_1_1session.html" title="Flow-IPC module providing the broad lifecycle and shared-resource organization – via the session conc...">ipc::session</a>-managed data to persist beyond the lifetime of a session-server process.</em> (Certainly such data can be maintained in the meta-application &ndash; but not as part of <a class="el" href="namespaceipc_1_1session.html" title="Flow-IPC module providing the broad lifecycle and shared-resource organization – via the session conc...">ipc::session</a>-managed data.)</li>
</ul>
</li>
</ul>
<p >About what kinds of data are we even speaking? Answer:</p><ul>
<li><em>Channels</em>: A given open <a class="el" href="classipc_1_1transport_1_1Channel.html" title="Peer to a bundle of 1-2 full-duplex pipe(s), one for transmitting unstructured binary blobs; the othe...">ipc::transport::Channel</a> (and thus <a class="el" href="classipc_1_1transport_1_1struc_1_1Channel.html">struc::Channel</a>) is by definition session-scope.<ul>
<li>It describes a bidirectional pipe between two processes, so if one of them goes away, the channel is no longer operational and cannot be reused.</li>
</ul>
</li>
<li><em>Structured-channel messages</em>: A given <a class="el" href="classipc_1_1transport_1_1struc_1_1Msg__in.html" title="A structured in-message instance suitable as received and emittable (to user) by struc::Channel.">ipc::transport::struc::Msg_in</a> or <a class="el" href="classipc_1_1transport_1_1struc_1_1Msg__out.html">struc::Msg_out</a> is often, but <em>not</em> necessarily, session-scope.<ul>
<li>A <code>struc::Msg_out</code> (a/k/a <a class="el" href="classipc_1_1transport_1_1struc_1_1Channel.html#a860cc99a701576879ebb5a1db58153cc" title="Encapsulation of any out-message payload sent or meant to be sent via send() (et al) by a *this of th...">ipc::transport::struc::Channel::Msg_out</a>) is a container of sorts, living usually in SHM. If you chose to put it into session-scope SHM arena, it is session-scope. If you chose (an) app-scope SHM arena, it is app-scope. The details of this are out of our scope here (it's an <a class="el" href="namespaceipc_1_1transport.html" title="Flow-IPC module providing transmission of structured messages and/or low-level blobs (and more) betwe...">ipc::transport</a> thing), but rest assured specifying which one you want is not difficult and involves no thinking about SHM, specifically.</li>
</ul>
</li>
<li><em>C++ data structures</em>: A given direct-SHM-stored C++ data structure &ndash; referred to via <code>shared_ptr&lt;T&gt;</code> a/k/a <code>Arena::Handle&lt;T&gt;</code> &ndash; is often, but <em>not</em> necessarily, session-scope.<ul>
<li>Much like with <code>struc::Msg_out</code>, but more explicitly specified on your part, this (potentially container-involving) data structure will live in a SHM arena of your choice. Spoiler alert (even though this is an <a class="el" href="namespaceipc_1_1transport.html" title="Flow-IPC module providing transmission of structured messages and/or low-level blobs (and more) betwe...">ipc::transport</a> topic): <code>Session::session_shm()-&gt;construct&lt;T&gt;()</code> = session-cope; <code>Session::app_shm()-&gt;construct&lt;T&gt;()</code> (or equivalently <code>Session_server::app_shm(app)-&gt;construct&lt;T&gt;()</code>, where <code>app</code> is a <code>Client_app</code>) = app-scope.</li>
</ul>
</li>
</ul>
<dl class="section note"><dt>Note</dt><dd>- Recall that we, generally, consider the latter bullet point (direct-stored-in-SHM data structures) an advanced use case. It has full support, and in fact is used internally a-la-eat-own-dog-food in our own structured-message implementation. That said one could argue that if one can represent IPC-shared data as <code>Msg_*</code>s exclusively, it is a good idea to do so. (There are, certainly, reasons to go beyond it.)</dd>
<dd>
- As for <code>Msg_*</code> (the middle bullet point), it is often entirely possible &ndash; and reasonable, as it tends to simplify algorithms &ndash; to only store them on-the-go, on the stack, letting them go out of scope after (as sender) creating/building or (as receiver) receiving/reading them. Such messages should always be session-scope but, from your point of view, don't really apply to this discussion at all. E.g., suppose you receive an message with the latest traffic stats for your HTTP server; synchronously report these to some central server (or whatever); and that's it. In that case your handler would receive an <a class="el" href="classipc_1_1transport_1_1struc_1_1Channel.html#ae1f4b316eaeb7ed520a84d9e21e21063" title="A ref-counted handle to Msg_in.">ipc::transport::struc::Channel::Msg_in_ptr</a> (a <code>shared_ptr&lt;Msg_in&gt;</code>), read from it via capnp-generated accessors, and then let the <code>Msg_in_ptr</code> lapse right there.</dd></dl>
<p><a class="anchor" id="recs"></a></p><h3>Organizing your objects: Up to sessions</h3>
<p >So, bottom line, you've got <code>Session</code> (possibly multiple), possibly a <code>Session_server</code>, <code>Channel</code>s and/or <code>struc::Channel</code>s, and then IPC-shared data (structured messages and/or C++ data structured). How to organize them in your code on each side? And, in a strongly related topic, what's the best way to hook up the <code>Session</code> error handler? Our recommendations follow. Again: it's the communicated principles that matter; the exact code snippets are an informal guide. Or, perhaps, it can be looked at a <em>case study</em> (of a reasonably complex situation) which you can morph for your own needs.</p>
<p >Let's have a (singleton, really) <code>class Process {}</code> just to bracket things nicely. (It's certainly optional but helps to communicate subsequent topics.) It applies to both the session-server app A and session-client app B. To do any <a class="el" href="namespaceipc_1_1session.html" title="Flow-IPC module providing the broad lifecycle and shared-resource organization – via the session conc...">ipc::session</a>-based IPC, you'll definitely need to set up your <code>App</code>s, <code>Client_app</code>s, and <code>Server_app</code>s &ndash; <a href="./session_setup.html#universe_desc">IPC universe description</a>, exactly as described in that link. As it says, run the same code at the start of each <code>Process</code> lifecycle (perhaps in its constructor). Store them in the <code>Process</code> as data members as needed, namely:</p><ul>
<li>Session-client <code>Process</code>:<ul>
<li>The local <code>Client_app</code> is needed each time a new <code>Session</code> is created so as to connect to the server; thus at least at startup and then whenever a session ends, and thus we need a new <code>Session</code>.</li>
<li>The opposing <code>Server_app</code> is needed at the exact same time also.</li>
</ul>
</li>
<li>Session-server <code>Process</code>:<ul>
<li>The local <code>Server_app</code> is needed when constructing the <code>Session_server</code>. This is normally done only once, and it is reasonable to do so in <code>Process</code> constructor; but if that action is delayed beyond that point, then you'll need the <code>Server_app</code> available and may wish to keep it as a data member in <code>Process</code>.</li>
<li>Same for the master <code>Client_app</code> list, which we in the past called <code>MASTER_APPS_AS_CLI</code>.</li>
<li>Lastly, each individial <code>Client_app</code> that is listed in <a class="el" href="structipc_1_1session_1_1Server__app.html#a12df649b33893270afeac7240a571633" title="A given Client_app (as identified by its distinct App::m_name) may request to establish an IPC sessio...">ipc::session::Server_app::m_allowed_client_apps</a> &ndash; as individual data members (like <code>Client_app m_cli_app_b</code> and <code>Client_app m_cli_app_c</code>), may be helpful for multi-client-application setups (i.e., if we are Ap, and Bp and Cp may establish sessions we us). For example, when deciding which part of your session-server application shall deal with a newly-opened <code>Session</code>, <code>Session::client_app()</code> is the actual <code>Client_app connecting_cli</code> as a handler argument. So then you could do, like, <code>if (session.client_app()-&gt;m_name == m_cli_app_b.m_name) { ...start the app Bp session... } else ...etc...</code>.</li>
</ul>
</li>
</ul>
<p >Whatever you do store should be listed in <code>Process { private: }</code> section first, as subsequent data items will need them.</p>
<p >Now it's time to set up some session(s). The more difficult task is on the server side; let us assume you do need to support multiple sessions concurrently. In this discussion we will assume your application's main loop is single-threaded. Your process will need a main-loop thread, and we will continue to assume the same proactor-pattern-with-Flow-IPC-internally-starting-threads-as-needed pattern as we have been (see <a class="el" href="async_loop.html">Asynchronicity and Integrating with Your Event Loop</a> for discussion including other possiblities). In this example I will use the <code>flow::async</code> API (as stated in the afore-linked Manual page, direct boost.asio use is similar, just with more boiler-plate). So, something like the following would work. Note we use the techniques explained in <a class="el" href="session_setup.html">Sessions: Setting Up an IPC Context</a> (and, to a lesser extent, <a class="el" href="chan_open.html">Sessions: Opening Channels</a>), but now in the context of our recommended organization of the program.</p>
<div class="fragment"><div class="line"><span class="keyword">class </span>Process : <span class="comment">// In session-server app A.</span></div>
<div class="line">  <span class="keyword">public</span> flow::log::Log_context</div>
<div class="line">{</div>
<div class="line"><span class="keyword">private</span>:</div>
<div class="line">  <span class="comment">// ...Your IPC universe description members go here....</span></div>
<div class="line"> </div>
<div class="line">  <span class="comment">// Alias your session-related types here.</span></div>
<div class="line">  <span class="keyword">template</span>&lt;ipc::session::schema::MqType S_MQ_TYPE_OR_NONE, <span class="keywordtype">bool</span> S_TRANSMIT_NATIVE_HANDLES,</div>
<div class="line">           <span class="keyword">typename</span> Mdt_payload = ::capnp::Void&gt;</div>
<div class="line">  <span class="keyword">using </span>Session_server_t = <a class="code hl_class" href="classipc_1_1session_1_1shm_1_1classic_1_1Session__server.html">ipc::session::shm::classic::Session_server&lt;S_MQ_TYPE_OR_NONE, S_TRANSMIT_NATIVE_HANDLES, Mdt_payload&gt;</a>;</div>
<div class="line">  <span class="keyword">using </span>Session_server = Session_server_t&lt;...knobs...&gt;;</div>
<div class="line">  <span class="keyword">using </span>Session = Session_server::Server_session_obj;</div>
<div class="line"> </div>
<div class="line">  <span class="comment">// Your main loop (a/k/a thread U).  All your code will be post()ed here.</span></div>
<div class="line">  <span class="comment">// Wrapping it in std::optional&lt;&gt; or unique_ptr&lt;&gt; may be helpful, depending on what you need to do first.</span></div>
<div class="line">  flow::async::Single_thread_task_loop m_worker;</div>
<div class="line"> </div>
<div class="line">  <span class="comment">// The big Kahuna.  m_session_srv-&gt;async_accept() will repeatedly give you a `Session`, at which point you will</span></div>
<div class="line">  <span class="comment">// in thread U kick-off its further handling.</span></div>
<div class="line">  std::optional&lt;Session_server&gt; m_session_srv;</div>
<div class="line"> </div>
<div class="line">  <span class="comment">// Accessed from thread U only, this is the target of the current m_session_srv-&gt;async_accept() of which there</span></div>
<div class="line">  <span class="comment">// is (in our example; and it is generally reasonable) one outstanding at a time.</span></div>
<div class="line">  Session m_next_session;</div>
<div class="line"> </div>
<div class="line"><span class="keyword">public</span>:</div>
<div class="line">  Process(...) :</div>
<div class="line">    flow::log::Log_context(...),</div>
<div class="line">    m_worker(get_logger(), <span class="stringliteral">&quot;srv_a_main&quot;</span>)</div>
<div class="line">  {</div>
<div class="line">    <span class="comment">// ...Load up IPC universe description around here....</span></div>
<div class="line"> </div>
<div class="line">    m_worker.start();</div>
<div class="line">    m_worker.post([<span class="keyword">this</span>]()</div>
<div class="line">    {</div>
<div class="line">      <span class="comment">// We are in thread U.  From now on we do work here.</span></div>
<div class="line">      <a class="code hl_typedef" href="namespaceipc.html#aa3192e586cc45d3e7c22463bf2760f89">Error_code</a> err_code;</div>
<div class="line">      m_session_srv.emplace(get_logger(), ...server_app_describing_us..., ...master_apps_as_cli...,</div>
<div class="line">                            &amp;err_code); <span class="comment">// Can also let it throw (pass in null here).</span></div>
<div class="line">      if (err_code)</div>
<div class="line">      {</div>
<div class="line">        m_session_srv.reset(); <span class="comment">// It did not initialize, so it is useless.</span></div>
<div class="line"> </div>
<div class="line">        <span class="comment">// Problem setting up server are pretty common when first trying this: permissions, etc.</span></div>
<div class="line">        <span class="comment">// In any case we can&#39;t really continue at all.  See Session_server doc headers for details.</span></div>
<div class="line"> </div>
<div class="line">        <span class="comment">// ...Get out, in whatever style makes sense for you....  Possibly just log and exit().</span></div>
<div class="line"> </div>
<div class="line">        <span class="keywordflow">return</span>;</div>
<div class="line">      }</div>
<div class="line">      <span class="comment">// else Ready to accept sessions.  Thus:</span></div>
<div class="line"> </div>
<div class="line">      m_session_srv-&gt;async_accept(&amp;m_next_session,</div>
<div class="line">                                  ..., <span class="comment">// Possibly more args, depending on the complexity of your setup.</span></div>
<div class="line">                                  [<span class="keyword">this</span>](<span class="keyword">const</span> <a class="code hl_typedef" href="namespaceipc.html#aa3192e586cc45d3e7c22463bf2760f89">Error_code</a>&amp; err_code)</div>
<div class="line">      {</div>
<div class="line">        <span class="keywordflow">if</span> (err_code != <a class="code hl_enumvalue" href="namespaceipc_1_1session_1_1error.html#a0708d4bec9ae853f4d7e3d7822fc8f2fafe29343445a7bf167cc186a44bd2c6ed">ipc::session::error::Code::S_OBJECT_SHUTDOWN_ABORTED_COMPLETION_HANDLER</a>)</div>
<div class="line">        {</div>
<div class="line">          m_worker.post([<span class="keyword">this</span>, err_code]() { on_new_session(err_code); });</div>
<div class="line">        }</div>
<div class="line">      });</div>
<div class="line">    }); <span class="comment">// m_worker.post()</span></div>
<div class="line">  } <span class="comment">// Process()</span></div>
<div class="line"> </div>
<div class="line"><span class="keyword">private</span>:</div>
<div class="line">  <span class="keywordtype">void</span> on_new_session(<span class="keyword">const</span> <a class="code hl_typedef" href="namespaceipc.html#aa3192e586cc45d3e7c22463bf2760f89">Error_code</a>&amp; err_code)</div>
<div class="line">  {</div>
<div class="line">    <span class="keywordflow">if</span> (err_code)</div>
<div class="line">    {</div>
<div class="line">      <span class="comment">// async_accept() failed.  This is fairly unusual and worth noting/logging/alerting, as it indicates</span></div>
<div class="line">      <span class="comment">// some kind of setup or environmental or transient problem worth looking into.  For more detail about</span></div>
<div class="line">      <span class="comment">// how to best deal with it, see Session_server::async_accept() doc header.  It is not fatal though.</span></div>
<div class="line"> </div>
<div class="line">      <span class="comment">// ...</span></div>
<div class="line">    }</div>
<div class="line">    <span class="keywordflow">else</span></div>
<div class="line">    {</div>
<div class="line">      <span class="comment">// else: Success!</span></div>
<div class="line"> </div>
<div class="line">      process_session(); <span class="comment">// We&#39;ll explain what do inside here, next.</span></div>
<div class="line">      <span class="comment">// m_next_session is empty again.</span></div>
<div class="line">    }</div>
<div class="line"> </div>
<div class="line">    <span class="comment">// Either way -- whether we just kicked off doing work on a new session, or something bad happened instead --</span></div>
<div class="line">    <span class="comment">// we will async-accept the next session whenever that may be ready to go.  Just like the initial one in ctor:</span></div>
<div class="line">    m_session_srv-&gt;async_accept(&amp;m_next_session,</div>
<div class="line">                                ...,</div>
<div class="line">                                [<span class="keyword">this</span>](<span class="keyword">const</span> <a class="code hl_typedef" href="namespaceipc.html#aa3192e586cc45d3e7c22463bf2760f89">Error_code</a>&amp; err_code)</div>
<div class="line">    {</div>
<div class="line">      <span class="keywordflow">if</span> (err_code != <a class="code hl_enumvalue" href="namespaceipc_1_1session_1_1error.html#a0708d4bec9ae853f4d7e3d7822fc8f2fafe29343445a7bf167cc186a44bd2c6ed">ipc::session::error::Code::S_OBJECT_SHUTDOWN_ABORTED_COMPLETION_HANDLER</a>)</div>
<div class="line">      {</div>
<div class="line">        m_worker.post([<span class="keyword">this</span>, err_code]() { on_new_session(err_code); });</div>
<div class="line">      }</div>
<div class="line">    });</div>
<div class="line">  } <span class="comment">// void on_new_session()</span></div>
<div class="line">}; <span class="comment">// class Process</span></div>
<div class="ttc" id="aclassipc_1_1session_1_1shm_1_1classic_1_1Session__server_html"><div class="ttname"><a href="classipc_1_1session_1_1shm_1_1classic_1_1Session__server.html">ipc::session::shm::classic::Session_server</a></div><div class="ttdoc">This is to vanilla Session_server what shm::classic::Server_session is to vanilla Server_session: it ...</div><div class="ttdef"><b>Definition:</b> session_server.hpp:74</div></div>
<div class="ttc" id="anamespaceipc_1_1session_1_1error_html_a0708d4bec9ae853f4d7e3d7822fc8f2fafe29343445a7bf167cc186a44bd2c6ed"><div class="ttname"><a href="namespaceipc_1_1session_1_1error.html#a0708d4bec9ae853f4d7e3d7822fc8f2fafe29343445a7bf167cc186a44bd2c6ed">ipc::session::error::Code::S_OBJECT_SHUTDOWN_ABORTED_COMPLETION_HANDLER</a></div><div class="ttdeci">@ S_OBJECT_SHUTDOWN_ABORTED_COMPLETION_HANDLER</div><div class="ttdoc">Async completion handler is being called prematurely, because underlying object is shutting down,...</div></div>
<div class="ttc" id="anamespaceipc_html_aa3192e586cc45d3e7c22463bf2760f89"><div class="ttname"><a href="namespaceipc.html#aa3192e586cc45d3e7c22463bf2760f89">ipc::Error_code</a></div><div class="ttdeci">flow::Error_code Error_code</div><div class="ttdoc">Short-hand for flow::Error_code which is very common.</div><div class="ttdef"><b>Definition:</b> common.hpp:298</div></div>
</div><!-- fragment --><p >To summarize: There are concurrent algorithms in play here, executing in interleaved async fashion via thread U (<code>m_worker</code>):</p><ul>
<li>First, initialize IPC universe description; and start <code>Session_server m_session_srv</code>. Then begin loop:</li>
<li>Algorithm 1 (one running throughout): Ask <code>m_session_srv</code> to accept the next session in the background. Once ready, give it to algorithm 2; and repeat.<ul>
<li>Kicked off in ctor.</li>
<li>Resumed in each <code>on_new_session()</code> invocation.</li>
</ul>
</li>
<li>Algorithm 2 (1+ parallel ones, each started in successful <code>on_new_session()</code>): Do the actual work of communicating with the opposing process, long-term, until one of us exits/aborts/becomes unhealthy.<ul>
<li>Kicked off in each successful <code>on_new_session()</code> invocation.</li>
<li>The meat of it is in <code>process_session()</code> which we will describe <a class="el" href="session_app_org.html#sessions_down">next</a>.</li>
</ul>
</li>
</ul>
<p >What about the client side? It is simpler, as there are no concurrent sessions; at most just one at a given time; plus every connect attempt is a straightforward non-blocking call that either immediately succeeds or immediately fails (the latter usually meaning no server is active/listening).</p>
<div class="fragment"><div class="line"><span class="keyword">class </span>Process : <span class="comment">// In session-client app B.</span></div>
<div class="line">  <span class="keyword">public</span> flow::log::Log_context</div>
<div class="line">{</div>
<div class="line"><span class="keyword">private</span>:</div>
<div class="line">  <span class="comment">// ...Your IPC universe description members go here... namely at least:</span></div>
<div class="line">  <a class="code hl_struct" href="structipc_1_1session_1_1Server__app.html">ipc::session::Server_app</a> m_srv_app;</div>
<div class="line">  <a class="code hl_struct" href="structipc_1_1session_1_1Client__app.html">ipc::session::Client_app</a> m_cli_app;</div>
<div class="line"> </div>
<div class="line">  <span class="comment">// Alias your session-related types here.</span></div>
<div class="line">  <span class="keyword">template</span>&lt;ipc::session::schema::MqType S_MQ_TYPE_OR_NONE, <span class="keywordtype">bool</span> S_TRANSMIT_NATIVE_HANDLES,</div>
<div class="line">           <span class="keyword">typename</span> Mdt_payload = ::capnp::Void&gt;</div>
<div class="line">  <span class="keyword">using </span>Client_session_t = <a class="code hl_class" href="classipc_1_1session_1_1shm_1_1classic_1_1Session__mv.html">ipc::session::shm::classic::Client_session&lt;S_MQ_TYPE_OR_NONE, S_TRANSMIT_NATIVE_HANDLES, Mdt_payload&gt;</a>;</div>
<div class="line">  <span class="keyword">using </span>Session = Client_session_t&lt;...knobs...&gt;;</div>
<div class="line"> </div>
<div class="line">  <span class="comment">// Your main loop (a/k/a thread U).  All your code will be post()ed here.</span></div>
<div class="line">  <span class="comment">// Wrapping it in std::optional&lt;&gt; or unique_ptr&lt;&gt; may be helpful, depending on what you need to do first.</span></div>
<div class="line">  flow::async::Single_thread_task_loop m_worker;</div>
<div class="line"> </div>
<div class="line">  <span class="comment">// Accessed from thread U only, this is -- at a given point in time, not counting non-blocking synchronous</span></div>
<div class="line">  <span class="comment">// calls into `Client_session::sync_connect() -- either (1) sitting there empty (as-if default-constructed),</span></div>
<div class="line">  <span class="comment">// because *m_active_session is currently open; or (2) sitting there empty (as-if default-constructed), because</span></div>
<div class="line">  <span class="comment">// the last `.sync_connect()` failed (server inactive/not listening), so we&#39;re waiting a bit to check again.</span></div>
<div class="line">  Session m_next_session;</div>
<div class="line"> </div>
<div class="line">  <span class="comment">// You&#39;ll see in the next section.  This shall encapsulate the active Session and your other IPC-involved algorithms and data.</span></div>
<div class="line">  std::optional&lt;App_session&gt; m_active_session;</div>
<div class="line"> </div>
<div class="line"><span class="keyword">public</span>:</div>
<div class="line">  Process(...) :</div>
<div class="line">    flow::log::Log_context(...),</div>
<div class="line">    m_worker(get_logger(), <span class="stringliteral">&quot;cli_b_main&quot;</span>)</div>
<div class="line">  {</div>
<div class="line">    <span class="comment">// ...Load up IPC universe description around here... namely at least:</span></div>
<div class="line">    <span class="comment">//   - m_srv_app</span></div>
<div class="line">    <span class="comment">//   - m_cli_app</span></div>
<div class="line"> </div>
<div class="line">    m_worker.start();</div>
<div class="line">    m_worker.post([<span class="keyword">this</span>]()</div>
<div class="line">    {</div>
<div class="line">      <span class="comment">// We are in thread U.  From now on we do work here.</span></div>
<div class="line">      open_next_session();</div>
<div class="line">    });</div>
<div class="line">  }</div>
<div class="line"> </div>
<div class="line"><span class="keyword">private</span>:</div>
<div class="line">  <span class="keywordtype">void</span> open_next_session()</div>
<div class="line">  {</div>
<div class="line">    <span class="comment">// When not connecting, m_session is empty.  We are about to connect, so let&#39;s get it into shape.</span></div>
<div class="line">    <span class="comment">// Note Session is (efficiently) move()able.</span></div>
<div class="line">    m_next_session</div>
<div class="line">      = Session(get_logger(), m_cli_app, m_srv_app,</div>
<div class="line">                ..., <span class="comment">// Possible channel-passive-open handler (discussed elsewhere).</span></div>
<div class="line">                [<span class="keyword">this</span>](<span class="keyword">const</span> <a class="code hl_typedef" href="namespaceipc.html#aa3192e586cc45d3e7c22463bf2760f89">Error_code</a>&amp; err_code)</div>
<div class="line">    {</div>
<div class="line">      m_worker.post([<span class="keyword">this</span>, err_code]()</div>
<div class="line">      {</div>
<div class="line">        <span class="comment">// We&#39;ll get into this later; but this is relevant only once m_next_session.sync_connect() succeeds.</span></div>
<div class="line">        <span class="comment">// Spoiler alert: Because the handler must be supplied before sync_connect(), we pre-hook-this up;</span></div>
<div class="line">        <span class="comment">// but the real handling of the error in an open session will be in App_session m_active_session.</span></div>
<div class="line">        <span class="comment">// We just forward it; otherwise it&#39;s not in the purview of class Process:</span></div>
<div class="line">        m_active_session-&gt;on_session_hosed(err_code);</div>
<div class="line">      });</div>
<div class="line">    });</div>
<div class="line"> </div>
<div class="line">    <span class="comment">// It&#39;s not doing anything; now we attempt connect.  Don&#39;t worry; that forwarding error handler does not</span></div>
<div class="line">    <span class="comment">// matter yet -- only once the sync_connect() succeeds.</span></div>
<div class="line">    <a class="code hl_typedef" href="namespaceipc.html#aa3192e586cc45d3e7c22463bf2760f89">Error_code</a> err_code;</div>
<div class="line">    m_next_session.sync_connect(&amp;err_code); <span class="comment">// This is a synchronous, non-blocking call!</span></div>
<div class="line">    <span class="keywordflow">if</span> (err_code)</div>
<div class="line">    {</div>
<div class="line">      <span class="comment">// sync_connect() failed.  Assuming everything is configured okay, this would only happen</span></div>
<div class="line">      <span class="comment">// if the opposing server is currently inactive.  Therefore it&#39;s not a great idea to immediately</span></div>
<div class="line">      <span class="comment">// sync_connect() again.  A reasonable plan is to schedule another attempt in 250-5000ms.</span></div>
<div class="line">      <span class="comment">// If the app need not do anything else in the meantime, it could just this_thread::sleep_*()</span></div>
<div class="line">      <span class="comment">// between attempts in a loop; otherwise use async scheduling, such as via flow::async&#39;s</span></div>
<div class="line">      <span class="comment">// m_worker.schedule_from_now() or boost.asio&#39;s timers.</span></div>
<div class="line">      <span class="comment">// Details left as exercise to reader.</span></div>
<div class="line"> </div>
<div class="line">      <span class="comment">// ...;</span></div>
<div class="line">      <span class="keywordflow">return</span>;</div>
<div class="line">    }</div>
<div class="line">    <span class="comment">// else: Success!  m_next_session is ready to go.</span></div>
<div class="line"> </div>
<div class="line">    process_session(); <span class="comment">// We&#39;ll explain what to do inside here, next.</span></div>
<div class="line">    <span class="comment">// m_next_session is empty (as-if default-cted) again.</span></div>
<div class="line">    <span class="comment">// It&#39;ll stay that way, until the stuff kicked-off in process_session() detects the death of the</span></div>
<div class="line">    <span class="comment">// Session we just opened.  Then, we&#39;ll call open_next_session() again.</span></div>
<div class="line">  } <span class="comment">// open_next_session()</span></div>
<div class="line">}; <span class="comment">// class Process</span></div>
<div class="ttc" id="aclassipc_1_1session_1_1shm_1_1classic_1_1Session__mv_html"><div class="ttname"><a href="classipc_1_1session_1_1shm_1_1classic_1_1Session__mv.html">ipc::session::shm::classic::Session_mv</a></div><div class="ttdoc">Implements the SHM-related API common to shm::classic::Server_session and shm::classic::Client_sessio...</div><div class="ttdef"><b>Definition:</b> session.hpp:44</div></div>
<div class="ttc" id="astructipc_1_1session_1_1Client__app_html"><div class="ttname"><a href="structipc_1_1session_1_1Client__app.html">ipc::session::Client_app</a></div><div class="ttdoc">An App that is used as a client in at least one client-server IPC split.</div><div class="ttdef"><b>Definition:</b> app.hpp:185</div></div>
<div class="ttc" id="astructipc_1_1session_1_1Server__app_html"><div class="ttname"><a href="structipc_1_1session_1_1Server__app.html">ipc::session::Server_app</a></div><div class="ttdoc">An App that is used as a server in at least one client-server IPC split.</div><div class="ttdef"><b>Definition:</b> app.hpp:206</div></div>
</div><!-- fragment --><p >Certainly both snippets leave open questions, and on both sides they concern <code>process_session()</code>. So let's get into that.</p>
<p ><a class="anchor" id="sessions_down"></a></p><h3>Organizing your objects: Sessions and lower</h3>
<p >It's tempting to type out each <code>Process::process_session()</code>, but it's a better idea to talk about the mysterious <code>App_session</code> seen in the last snippet. The organization of and around <code>App_session</code> is possibly the most valuable principle to communicate, as doing the right stuff in this context is arguably the least obvious and most life-easing challenge.</p>
<p >The proposed class <code>App_session</code> is <em>your</em> application's encapsulation of everything to do with a given <em>open</em> session. Clearly this will include the <a class="el" href="namespaceipc_1_1session.html" title="Flow-IPC module providing the broad lifecycle and shared-resource organization – via the session conc...">ipc::session</a> <code>Session</code> itself, but also all your per-session data and algorithms. Its lifetime shall equal that of the <code>Session</code> remaining open. Some key points:</p><ul>
<li>Just as &ndash; once open &ndash; <a class="el" href="namespaceipc_1_1session.html#ab01d5bce0ab2f3435ca9dd88d27609dc" title="A vanilla Server_session with no optional capabilities.">ipc::session::Server_session</a> and <a class="el" href="namespaceipc_1_1session.html#a77d8fda00e1e17f595a6a5d4c44375e7" title="A vanilla Client_session with no optional capabilities.">ipc::session::Client_session</a> have identical public capabilities/APIs, so should the two mutually-facing <code>App_session</code>s. (Of course, internally, they will have typically different/complementary duties, depending on what your design is.) In particular:<ul>
<li>Each should take-over (via move-semantics) an already-open <code>Session</code> and manage it (e.g., opening channels; using SHM), until the session is hosed (next bullet point).</li>
<li>Each should be ready for that <code>Session</code> to report being <em>hosed</em> and then report that to the "parent" <code>Process</code>; which should at that point <em>destroy</em> the <code>App_session</code> and everything in it (including the taken-over <code>Session</code>) immediately.</li>
</ul>
</li>
</ul>
<p >Because <code>App_session</code> on each side is, conceptually, the same (as a black-box), ideally they should have the same APIs. This isn't any kind of hard requirement, as these <em>are</em> separate applications, and no polymorphism of any kind will actually be used; but it is a good rule of thumb and helps ensure a clean design.</p>
<p >Therefore we will <em>begin</em> by describing <code>App_session</code>. So what about <code>process_session()</code>? Answer: It's just a bit of logical glue between <code>App_session</code> and the mutually-different patterns in a server <code>Process</code> versus client <code>Process</code>. Thus we'll finish this part of the discussion by describing <code>process_session()</code>. On to <code>App_session</code>:</p>
<div class="fragment"><div class="line"><span class="comment">// Similar, or identical, API (and the listed logic/data) regardless of whether this is application Ap (session-server) or Bp (session-client).</span></div>
<div class="line"><span class="keyword">class </span>Process::App_session</div>
<div class="line">{</div>
<div class="line"><span class="keyword">private</span>:</div>
<div class="line">  <span class="comment">// In our example we use a single worker thread together with parent Process.</span></div>
<div class="line">  flow::async::Single_thread_task_loop&amp; m_worker;</div>
<div class="line"> </div>
<div class="line">  <span class="comment">// The open Session we take-over from Process.</span></div>
<div class="line">  Session m_session;</div>
<div class="line"> </div>
<div class="line">  <span class="comment">// How we inform Process that they should delete us.</span></div>
<div class="line">  <a class="code hl_typedef" href="namespaceipc_1_1util.html#a31e67d3a017477a04b313927e2f2c733">ipc::util::Task</a> m_on_session_closed_func;</div>
<div class="line"> </div>
<div class="line">  <span class="comment">// ...ATTENTION!  IPC-relevant (per-session) data/handles shall be listed here -- *after* the above,</span></div>
<div class="line">  <span class="comment">// especially after m_session.  Thus, they will be destroyed (when `*this` is destroyed) in the opposite order.</span></div>
<div class="line">  <span class="comment">// `Session` should always be destroyed after per-session items.  This is, at a minimum, the clean thing to do;</span></div>
<div class="line">  <span class="comment">// and in some cases -- including, likely, shared_ptr&lt;&gt; handles into SHM -- it will avoid memory corruptions and</span></div>
<div class="line">  <span class="comment">// crashes....</span></div>
<div class="line">  <span class="comment">//</span></div>
<div class="line">  <span class="comment">// Corollary: If you, for whatever reason, wrap m_session in a unique_ptr or std::optional&lt;&gt; or the like, and</span></div>
<div class="line">  <span class="comment">// decide to `.reset()` that thing manually (perhaps in explicit ~App_session()), then make sure you</span></div>
<div class="line">  <span class="comment">// do so only *after* these data members are first destroyed/nullified.</span></div>
<div class="line"> </div>
<div class="line"><span class="keyword">public</span>:</div>
<div class="line">  App_session(..., flow::async::Single_thread_task_loop* worker) :</div>
<div class="line">    m_worker(*worker)</div>
<div class="line">  {</div>
<div class="line">    <span class="comment">// Do not start work yet.  They must call go().</span></div>
<div class="line">  }</div>
<div class="line"> </div>
<div class="line">  <span class="keyword">template</span>&lt;<span class="keyword">typename</span> On_session_closed_func&gt;</div>
<div class="line">  <span class="keywordtype">void</span> go(Session&amp;&amp; session, On_session_closed_func&amp;&amp; on_session_closed_func)</div>
<div class="line">  {</div>
<div class="line">    m_session = std::move(session); <span class="comment">// Session inside Process has been emptied now.</span></div>
<div class="line">    m_on_session_closed_func = std::move(on_session_closed_func);</div>
<div class="line"> </div>
<div class="line">    <span class="comment">// ...Ready to work!  From this point on, do what needs to be done: open channels via m_session.open_channel(),</span></div>
<div class="line">    <span class="comment">// use already-opened init-channels, use m_session.session_shm() and m_session.app_shm(), etc.</span></div>
<div class="line">    <span class="comment">// Obviously the specifics will differ depending on whether this is application Ap or Bp... after all, they</span></div>
<div class="line">    <span class="comment">// are different applications!</span></div>
<div class="line">  }</div>
<div class="line"> </div>
<div class="line">  <span class="comment">// `Process` promises to call this (which would occur at most once), if m_session reports error.</span></div>
<div class="line">  <span class="comment">// We&#39;d do it ourselves, but it&#39;s done differently depending on client-versus-server, and we try to leave such</span></div>
<div class="line">  <span class="comment">// differing details outside App_session.</span></div>
<div class="line">  <span class="keywordtype">void</span> on_session_hosed(<span class="keyword">const</span> <a class="code hl_typedef" href="namespaceipc.html#aa3192e586cc45d3e7c22463bf2760f89">Error_code</a>&amp; err_code)</div>
<div class="line">  {</div>
<div class="line">    <span class="comment">// ...Do stuff, possibly nothing, knowing that m_session is unusable, and we can no longer open channels or</span></div>
<div class="line">    <span class="comment">// use per-session SHM.  Technically, `Channel`s (and thus `struc::Channel`s) operate independently of</span></div>
<div class="line">    <span class="comment">// the source Session -- if any -- but a Session being hosed indicates, at best, ill health of the opposing</span></div>
<div class="line">    <span class="comment">// process; therefore it is best to not touch them from this point on....</span></div>
<div class="line"> </div>
<div class="line">    <span class="comment">// Then, inform Process via the callback it provided.  This should cause it to destroy *this.</span></div>
<div class="line">    <span class="comment">// (There are certainly other ways of organizing this, but this way works as well as any.)</span></div>
<div class="line">    m_worker-&gt;post([<span class="keyword">this</span>]() { m_on_session_closed_func(); });</div>
<div class="line">  }</div>
<div class="line">};</div>
<div class="ttc" id="anamespaceipc_1_1util_html_a31e67d3a017477a04b313927e2f2c733"><div class="ttname"><a href="namespaceipc_1_1util.html#a31e67d3a017477a04b313927e2f2c733">ipc::util::Task</a></div><div class="ttdeci">flow::async::Task Task</div><div class="ttdoc">Short-hand for polymorphic function (a-la std::function&lt;&gt;) that takes no arguments and returns nothin...</div><div class="ttdef"><b>Definition:</b> util_fwd.hpp:122</div></div>
</div><!-- fragment --><p >Simple, really. So what's the big deal? Answer: Mainly the big deal is: the per-session objects (where it says <code>ATTENTION!</code>) are encapsulated cleanly at the same level as the <code>Session</code>: they are thus destroyed/nullified at the same time (in fact, the latter before the former).</p>
<p >So now all that's left is to hook this up to the rest of <code>Process</code>, in each application: <code>process_session()</code>. That's where things are somewhat different &ndash; since the session is open almost-but-not-quite, and the API somewhat differs between the two. Client first:</p>
<div class="fragment"><div class="line"><span class="comment">// In session-client app Bp.</span></div>
<div class="line"><span class="keywordtype">void</span> Process::process_session()</div>
<div class="line">{</div>
<div class="line">  <span class="comment">// App_session m_active_session is null; now we have the Session m_next_session it should take-over, so</span></div>
<div class="line">  <span class="comment">// we can construct the App_session.</span></div>
<div class="line">  m_active_session.emplace(..., &amp;m_worker);</div>
<div class="line"> </div>
<div class="line">  m_active_session-&gt;go(std::move(m_next_session), <span class="comment">// Let it eat it, making it empty (as-if default-cted) again.</span></div>
<div class="line">                       [<span class="keyword">this</span>]()</div>
<div class="line">  {</div>
<div class="line">    <span class="comment">// As promised -- destroy the App_session, together with all its stuff.</span></div>
<div class="line">    m_active_session.reset();</div>
<div class="line"> </div>
<div class="line">    <span class="comment">// ...and start trying to open the next one.</span></div>
<div class="line">    open_next_session();</div>
<div class="line">  });</div>
<div class="line"> </div>
<div class="line">  <span class="comment">// That&#39;s it!  We&#39;ve already set up m_active_session-&gt;on_session_hosed() back in open_next_session().</span></div>
<div class="line">  <span class="comment">// We provided the on-session-closed logic just above.  And in the client-side API the Session is open upon</span></div>
<div class="line">  <span class="comment">// successful sync_connect().  The rest is up to App_session in the mean-time.</span></div>
<div class="line">}</div>
</div><!-- fragment --><p >Server is somewhat more complex as usual.</p>
<div class="fragment"><div class="line"><span class="comment">// In session-server app Ap.</span></div>
<div class="line"><span class="keyword">class </span>Process</div>
<div class="line">{</div>
<div class="line"><span class="keyword">private</span>:</div>
<div class="line">  <span class="keyword">using </span>App_session_ptr = shared_ptr&lt;App_session&gt;;</div>
<div class="line"> </div>
<div class="line">  <span class="comment">// This may or may not be necessary.  It is possible to just capture it in a lambda and not save it in</span></div>
<div class="line">  <span class="comment">// `*this` directly.  Still it might be nice; like for instance m_app_sessions.size() can be logged/reported</span></div>
<div class="line">  <span class="comment">// periodically.</span></div>
<div class="line">  unordered_set&lt;App_session_ptr&gt; m_app_sessions;</div>
<div class="line"> </div>
<div class="line">  <span class="keywordtype">void</span> process_session()</div>
<div class="line">  {</div>
<div class="line">    <span class="comment">// Similarly to client, App_session requires:</span></div>
<div class="line">    <span class="comment">//   -# an open Session to take-over;</span></div>
<div class="line">    <span class="comment">//   -# that its -&gt;on_session_hosed() is invoked at the proper time (when Session reports it);</span></div>
<div class="line">    <span class="comment">//   -# that it knows how to tell us it&#39;s ready to be destroyed (and that we do the right thing at that point).</span></div>
<div class="line">    <span class="comment">//</span></div>
<div class="line">    <span class="comment">// The first one just requires that we call session.init_handlers().</span></div>
<div class="line">    <span class="comment">// (The second one is accomplished via that call.)</span></div>
<div class="line">    <span class="comment">// The third one, same as on the client side, is an explicit function argument to App_session::go().</span></div>
<div class="line"> </div>
<div class="line">    <span class="comment">// Create the App_session; note the ctor does not require `session` (go() does).</span></div>
<div class="line">    <span class="keyword">auto</span> app_session = make_shared&lt;App_session&gt;(..., &amp;m_worker);</div>
<div class="line">    m_app_sessions.insert(app_session); <span class="comment">// Save it, since we track them in a set.  (May not be necessary.)</span></div>
<div class="line"> </div>
<div class="line">    <span class="comment">// Last step required for Session becoming open, as go() needs.</span></div>
<div class="line">    m_next_session-&gt;init_handlers(..., <span class="comment">// Possible channel-passive-open handler (discussed elsewhere).</span></div>
<div class="line">                                  [<span class="keyword">this</span>, app_session]</div>
<div class="line">                                    (<span class="keyword">const</span> <a class="code hl_typedef" href="namespaceipc.html#aa3192e586cc45d3e7c22463bf2760f89">Error_code</a>&amp; err_code) <span class="keyword">mutable</span></div>
<div class="line">    {</div>
<div class="line">      m_worker.post([<span class="keyword">this</span>, app_session = std::move(app_session), err_code]()</div>
<div class="line">      {</div>
<div class="line">        app_session-&gt;on_session_hosed(err_code); <span class="comment">// Just forward it; same as in client.</span></div>
<div class="line">      });</div>
<div class="line">    });</div>
<div class="line"> </div>
<div class="line">    <span class="comment">// And supply the on-app-session-closed logic.</span></div>
<div class="line">    app_session-&gt;go(std::move(m_next_session), <span class="comment">// Let it eat it, making it empty (as-if default-cted) again.</span></div>
<div class="line">                    [<span class="keyword">this</span>, app_session]()</div>
<div class="line">    {</div>
<div class="line">      <span class="comment">// It&#39;s going away.</span></div>
<div class="line">      m_app_sessions.erase(app_session);</div>
<div class="line"> </div>
<div class="line">      <span class="comment">// As promised -- destroy the App_session, together with all its stuff.</span></div>
<div class="line">      app_session.reset();</div>
<div class="line"> </div>
<div class="line">      <span class="comment">// That&#39;s it.  Process main loop already expects more (possibly concurrent) `Session`s arriving whenever.</span></div>
<div class="line">    });</div>
<div class="line">  } <span class="comment">// process_session()</span></div>
<div class="line">};</div>
</div><!-- fragment --><p >The separation of App_session ctor and <code>go()</code> ensures that the differently-ordered APIs around session opening on the server side versus client side are both supported. It is, for example, entirely possible to swap the bodies of <code>App_session::go()</code> (and all that they invoke, <code>private</code>ly) between the two, thus swapping their duties &ndash; once the session is open. As we've emphasized before: The capabilities of a <code>Session</code> &ndash; once open &ndash; are identical, and the session-server versus session-client identity no longer matters to Flow-IPC. We've decided on a reasonably simple API for the user to use, so that <code>App_session</code> acts the same way.</p>
<h3>What did we miss?</h3>
<p >Answer: not much. What remains &ndash; assuming one grokked all of the above &ndash; can probably be left as an exercise for the reader. Nevertheless:</p>
<p >We've covered partner-triggered session closing, which is the hard part; what about locally-triggered? One way to approach it might be a SIGTERM/SIGINT handler outside <code>Process</code> that destroys said <code>Process</code>. On either side the <code>App_session</code> (or <code>App_session</code>s) will be destroyed, hence their stored open <code>Session</code>s will too. (The way we wrote the code in the server side, <code>app_session</code> is captured in a callback lambda saved by <code>m_worker</code>, but <code>m_worker</code> too will be destroyed with all its stored closures.) In the server side, the <code>Session_server</code> will also be destroyed as part of the process.</p>
<p >We ignored the case of a session-server that speaks with 2+ different client applications (Bp, Cp, ...). A good way to handle that would be to have multiple <code>App_session</code> classes, one for each partner application. <code>Process::process_session()</code> can then query <code>m_next_session.client_app()</code> and create/save/activate whichever one is appropriate based on that.</p>
<p >We skipped over channel-opening considerations.</p><ul>
<li>Init-channel object(s) or list(s) thereof could be passed-through into <code>App_session</code> in its constructor on each side.</li>
<li>We omitted the passive-open handler, on each side, in our case study. If it is necessary to use these, one would probably add <code>App_session::on_passive_channel_open(Channel&amp;&amp;, Mdt_reader_ptr)</code> and have the <code>Session</code> ctor (client-side) or <code>Session::init_handlers()</code> (server-side) forward to that. (We reiterate that, generally, init-channels are easier to set up, as it involves less asynchronicity on at least one side.)</li>
</ul>
<p >Lastly we omitted mention of app-scope (as opposed session-scope) data. In short, assuming you understand <a class="el" href="session_app_org.html#scope">scopes in general</a>, the above case-study code would be expanded as follows:</p><ul>
<li>Client side: Not much is different. All logic and data are still in <code>App_session</code>. Certainly the logic therein must be aware that some of data members in <code>App_session</code> would potentially refer to app-scope data, meaning data that would remain in SHM past the life of <code>*this App_session</code>, but the data members would still live in <code>App_session</code>. In a session-client there is no other place for them by their nature.</li>
<li>Server side: The session-server, most likely, would store app-scope (cross-session) data members outside <code>App_session</code>; perhaps in <code>Process</code>, or in some individual-<code>Client_app</code>-specific (possibly nested in <code>Process</code>) class. For example, if there is a memory cache object, it could be a data member in <code>Process</code>. In the opposing (client-side) <code>App_session</code> there might be a mirror data member.</li>
</ul>
<p >Unlike per-session algorithms and data, wherein the server and client are equal from Flow-IPC's point of view, by definition of app-scope there is an asymmetry here between the two sides. Generally one tries to avoid this, but that's the nature of the beast in this case.</p>
<hr  />
<p >And that's that. If you want to use <a class="el" href="namespaceipc_1_1session.html" title="Flow-IPC module providing the broad lifecycle and shared-resource organization – via the session conc...">ipc::session</a> &ndash; and unless you enjoy avoidable pain, you probably should &ndash; then the preceding pages provide more than enough information to proceed. What's left now are some odds and ends that can be quite important in production but are arguably not going to be a mainstream worry during the bulk of development and design. The next couple (or so) pages deal with such <a class="el" href="namespaceipc_1_1session.html" title="Flow-IPC module providing the broad lifecycle and shared-resource organization – via the session conc...">ipc::session</a> topics.</p>
<p >The next page is: <a class="el" href="safety_perms.html">Safety and Permissions</a>.</p>
<hr  />
<center><b>MANUAL NAVIGATION:</b> <a class="el" href="chan_open.html">Preceding Page</a> - <a class="el" href="safety_perms.html">Next Page</a> - <a href="./pages.html"><b>Table of Contents</b></a> - <a class="el" href="namespaceipc.html"><b>Reference</b></a></center> </div></div><!-- contents -->
</div><!-- PageDoc -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated on Thu May 2 2024 23:56:35 for Flow-IPC by&#160;<a href="https://www.doxygen.org/index.html"><img class="footer" src="doxygen.svg" width="104" height="31" alt="doxygen"/></a> 1.9.4
</small></address>
</body>
</html>
